﻿using System;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ExpressionLanguage
{
    public class ExpressionInterpreter
    {
		internal class Namespace
        {
            public static readonly string GLOBAL_PREFIX = "_G.";

            public Namespace()
            {
                SymbolPrefix_.Append(GLOBAL_PREFIX);
            }

            public string GetName( string name )
            {
                Debug.Assert(SymbolPrefix_.Length != 0);
                return SymbolPrefix_.ToString() + name;
            }

            public void EnterNamespace( string space )
            {
                Records_.AddLast(SymbolPrefix_.Length);
                SymbolPrefix_.Append(space);
                SymbolPrefix_.Append('.');
            }

            public void LeaveNamespace()
            {
                Debug.Assert(Records_.Count != 0);
                int index = Records_.Last.Value;
                SymbolPrefix_.Remove(index, SymbolPrefix_.Length - index);
                Records_.RemoveLast();
            }

            public bool HasMorePrefix()
            {
                return Records_.Count != 0;
            }

            public Namespace Clone()
            {
                Namespace ns = new Namespace();
                ns.SymbolPrefix_.Clear(); // Remove default _G. prefix
                ns.SymbolPrefix_.Append(SymbolPrefix_);
                foreach (int val in Records_)
                    ns.Records_.AddLast(val);
                return ns;
            }
			
			public string GetNamespaceFullName()
			{
				int length = SymbolPrefix_.Length;
				SymbolPrefix_.Remove (length-1,1);
				string ret = SymbolPrefix_.ToString();
				SymbolPrefix_.Append ('.');
				return ret;
			}

            private StringBuilder SymbolPrefix_ = new StringBuilder();
            private LinkedList<int> Records_ = new LinkedList<int>();
        }
		
        public class ExecutionStack
        {
            public struct Variable
            {
                public enum VariableType
                {
                    VOID,
                    STRING,
                    NUMBER,
                    OBJECT

                }
                // This function is probably useful when the user want to
                // define null keyword/function to represents that something
                // is void or null. Anyway. It is OK to do so here.
                public static Variable MakeVoid()
                {
                    Variable ret = new Variable();
                    ret.Token_ = VariableType.VOID;
                    return ret;
                }

                public Variable( object udata )
                    :this()
                {
                    Token_ = VariableType.OBJECT;
                    UserData_ = udata;
                }

                public Variable(string val)
                    : this()
                {
                    Token_ = VariableType.STRING;
                    StringValue_ = val;
                }

                public Variable(double val)
                    : this()
                {
                    Token_ = VariableType.NUMBER;
                    DoubleValue_ = val;
                }

                public VariableType Type
                {
                    get { return Token_; }
                }

                public bool Void
                {
                    get { return Token_ == VariableType.VOID; }
                }

                public bool AsBoolean( out bool val )
                {
                    val = default(bool);
                    if (Token_ == VariableType.NUMBER)
                    {
                        val = DoubleValue_ != 0.0f;
                        return true;
                    }
                    else
                        return false;
                }

                public bool AsNumber(out double val)
                {
                    val = default(double);
                    if (Token_ == VariableType.NUMBER)
                    {
                        val = DoubleValue_;
                        return true;
                    }
                    else
                        return false;
                }


                public bool AsString(out string val)
                {
                    val = default(string);
                    if (Token_ == VariableType.STRING)
                    {
                        val = StringValue_;
                        return true;
                    }
                    else
                        return false;
                }

                public object AsObject()
                {
                    if (Token_ == VariableType.OBJECT)
                        return UserData_;
                    else
                        return null;
                }

                public object ToObject()
                {
                    if (Token_ == VariableType.VOID)
                    {
                        return null;
                    }
                    else
                    {
                        switch(Token_)
                        {
                            case VariableType.OBJECT:
                                return UserData_;
                            case VariableType.NUMBER:
                                return (object)(DoubleValue_);
                            case VariableType.STRING:
                                return (object)(StringValue_);
                            default:
                                Debug.Assert(false);
                                return null;
                        }

                    }
                }


                private string StringValue_;
                private double DoubleValue_;
                private object UserData_;
                private VariableType Token_;
            }
			
			
            private string FormatKey( string key )
            {
                if( key.Length > 3 )
                {
                    // This is hard code comparison, once the GLOBAL_PREFIX
                    // changes, this code needs to be modified. FIX ME
                    if (key[0] == '_' && key[1] == 'G' && key[2] == '.')
                        return key;
                }

                return Namespace.GLOBAL_PREFIX + key;
            }

            public void AddNumber(string key, double num)
            {
                Add(key, new Variable(num));
            }

            public void AddString(string key, string str)
            {
                Add(key, new Variable(str));
            }

         

            public void AddObject(string key, object val)
            {
                Add(key, new Variable(val));
            }

            public void Add(string key, Variable variable)
            {
                GlobalVariableMap_[FormatKey(key)] = variable;
            }
			
			// DefineVariable function is used to define system level variable. This level will not
			// be removed even if you call Clear function. You should call Remove function to remove
			// these define variable one by one. The reason is that , there are some variables that
			// the user wants to use all the time instead of for specific execution. This variable 
			// should be defined as defined variable. EG, Math.PEI as constant variable .
			public bool DefineVariable( string key , Variable variable )
			{
				try
				{
					DefineVariableMap_.Add (key,variable);
				}
				catch( ArgumentException )
				{
					return false;
				}
				return true;
			}
			
			public void Clear()
			{
				GlobalVariableMap_.Clear ();
			}

            public bool Remove(string key)
            {
				string fullName = FormatKey (key);
                if(!GlobalVariableMap_.Remove(fullName))
					return DefineVariableMap_.Remove(fullName);
				return true;
            }

            public bool LookUpAsNumber(string key, out double number)
            {
                number = default(double);
                Variable variable;
                if(!LookUp (key,out variable))
					return false;
                if (!variable.AsNumber(out number))
                    return false;
                return true;
            }

            public bool LookUpAsString(string key, out string str)
            {
                str = default(string);
                Variable variable;
                if(!LookUp (key,out variable))
					return false;
                if (!variable.AsString(out str))
                    return false;
                return true;
            }

            public object LookUpAsObject(string key)
            {
                Variable variable;
                if (!LookUp(key, out variable))
                    return null;
                return variable.AsObject();
            }

            public bool LookUp(string key, out Variable variable)
            {
				
                variable = default(Variable);
				string fullName = FormatKey (key);
                if (!GlobalVariableMap_.TryGetValue(fullName, out variable))
                    if(!DefineVariableMap_.TryGetValue (fullName,out variable))
						return false;
                return true;
            }

            private IDictionary<string, Variable> GlobalVariableMap_ = new Dictionary<string,Variable>();
			private IDictionary<string,Variable> DefineVariableMap_ = new Dictionary<string,Variable>();

        }

        public class GlobalFunction
        {
            public delegate bool MainFunction(List<ExecutionStack.Variable> parameters, out ExecutionStack.Variable ReturnValue);

            public bool RegisterGlobalFunction(string key, MainFunction function)
            {
                try
                {
                    GlobalFunctionMap_.Add(key, function);
                }
                catch (ArgumentException)
                {
                    return false;
                }
                return true;
            }

            public bool RemoveGlobalFunction(string key)
            {
                return GlobalFunctionMap_.Remove(key);
            }

            public MainFunction GetFunction(string key)
            {
                MainFunction function;
                if (!GlobalFunctionMap_.TryGetValue(key, out function))
                    return null;
                return function;
            }
			
            private IDictionary<string, MainFunction> GlobalFunctionMap_ = new Dictionary<string, MainFunction>();
        }

        private struct ObjectName
        {
            public ObjectName(string relativeName)
                :this()
            {
                RelativeName_ = relativeName;
                Position_ = 0;
                Cut_ = true;
            }

            public bool HasMoreNames()
            {
                if (Position_ >= RelativeName_.Length)
                    return false;
                int ret = RelativeName_.IndexOf('.', Position_);
                if (ret == -1)
                {
                    ret = RelativeName_.Length;
                }
                if (!Cut_)
                    Next_ = RelativeName_.Substring(0, ret);
                else
                    Next_ = RelativeName_.Substring(Position_, ret - Position_);
                Position_ = ret + 1;
                return true;
            }

            public string NextName()
            {
                string ret = Next_;
                Next_ = null;
                return ret;
            }

            public void Cut()
            {
                Cut_ = true;
            }

            private bool Cut_;
            private int Position_;
            private string Next_;
            private string RelativeName_;
        }

        private static ErrorCollector ExecuteFunctionCall(ExpressionParser.AstNode_FunctionCall node, GlobalFunction func,
                                                           ExecutionStack stack, Namespace ns, out ExecutionStack.Variable result )
        {
            result = ExpressionInterpreter.ExecutionStack.Variable.MakeVoid();
            string functionName;
            if (!node.FunctionName.AsVariable(out functionName))
                return new ErrorCollector("Cannot get function name");
            GlobalFunction.MainFunction entry = func.GetFunction(functionName);
            if (entry == null)
                return new ErrorCollector("Cannot find function:" + functionName);
            List<ExecutionStack.Variable> parameters = new List<ExecutionStack.Variable>();
            foreach (var v in node.Parameters)
            {
                ExecutionStack.Variable par;
                ErrorCollector ret = ExecuteOperand(v, func, stack, ns ,out par);
                if (!ret.Success)
                    return ret;
                parameters.Add(par);
            }
            // Invoking the registered function
            if (!entry(parameters, out result))
                return new ErrorCollector("The function:" + functionName + " executes error");
            return new ErrorCollector();
        }

        private static bool IsBaseNode(ExpressionParser.AstNode node)
        {
            return node.Type == ExpressionParser.AstNode.NodeType.CONSTANT ||
                    node.Type == ExpressionParser.AstNode.NodeType.VARIABLE;
        }

        private static bool GetValueWithReflection(ExecutionStack stack, Namespace ns, string key, out ExecutionStack.Variable operand)
        {
            operand = default(ExecutionStack.Variable);
            ObjectName objName = new ObjectName(key);
            object obj = null;
            while (objName.HasMoreNames())
            {
                string name = objName.NextName();
                // Grab the variable and check weather it is an Object or not
               obj = stack.LookUpAsObject(ns.GetName(name));
                if( obj != null )
                {
                    break;
                }
            }
            if (obj == null) return false;
            // Cut the previous name right now.
            objName.Cut();

            if (!objName.HasMoreNames())
                return false;
            string fieldName = objName.NextName();
            // Now let's grab the value of obj
            while( true )
            {
                FieldInfo fieldInfo = obj.GetType().GetField(fieldName);
                if( fieldInfo == null )
                {
                    PropertyInfo propertyInfo = obj.GetType().GetProperty(fieldName);
                    if (propertyInfo == null)
                        return false;
                    // Checking if we have more field names or not
                    if( objName.HasMoreNames() )
                    {
                        fieldName = objName.NextName();
                        // Moving our object chain further
                        obj = propertyInfo.GetValue(obj, null);
                        continue;
                    }
                    else
                    {
                        // Last name we can get value here
                        operand = new ExecutionStack.Variable(propertyInfo.GetValue(obj, null));
                        return true;
                    }
                }
                else
                {
                    if(objName.HasMoreNames())
                    {
                        fieldName = objName.NextName();
                        obj = fieldInfo.GetValue(obj);
                        continue;
                    }
                    else
                    {
                        operand = new ExecutionStack.Variable(fieldInfo.GetValue(obj));
                        return true;
                    }
                }
            }
        }

        private static string IsObjectSymbol( string key )
        {
            if( key.Length > 1 )
            {
                if (key[0] == '@')
                    return key.Substring(1, key.Length - 1);
            }
            return null;
        }

        private static bool FetchOperand( ExpressionParser.AstNode node, GlobalFunction func, ExecutionStack stack,
                                          Namespace ns, out ExecutionStack.Variable operand )
        {
            operand = ExpressionInterpreter.ExecutionStack.Variable.MakeVoid();
            if (node.Type == ExpressionParser.AstNode.NodeType.CONSTANT)
            {
                ExpressionParser.AstNode_Constant constantNode = (ExpressionParser.AstNode_Constant)(node);
                switch (constantNode.Constant.Token)
                {
                    case ExpressionScanner.TToken.STRING:
                        string sval;
                        if (!constantNode.Constant.AsString(out sval))
                            return false;
                        operand = new ExecutionStack.Variable(sval);
                        return true;
                    case ExpressionScanner.TToken.NUMBER:
                        double dval;
                        if (!constantNode.Constant.AsNumber(out dval))
                            return false;
                        operand = new ExecutionStack.Variable(dval);
                        return true;
                    default:
                        return false;
                }
            }
            else if (node.Type == ExpressionParser.AstNode.NodeType.VARIABLE)
            {
                string key;
                ExpressionParser.AstNode_Variable variableNode =
                    (ExpressionParser.AstNode_Variable)node;
                if (!variableNode.Variable.AsVariable(out key))
                    return false;

                string objSymbol = IsObjectSymbol(key);
                if (objSymbol == null)
                {

                    // Looking up the value from both local namespace and global namespace
                    // In order to avoid clone, we do a fast fetch try.If that fails, we do
                    // a backtrack of the nested namespace.

                    if (stack.LookUp(ns.GetName(key), out operand))
                        return true;
                    Namespace tempNs = ns.Clone();
                    while (tempNs.HasMorePrefix())
                    {
                        tempNs.LeaveNamespace();
                        if (stack.LookUp(tempNs.GetName(key), out operand))
                            return true;
                    }
                    return false;
                }
                else
                {
                    // Once we find out that such name is not existed in our stack. We may think such name
                    // is an C# object name and possibly we can use reflection to grab the value here. 
                    if (!GetValueWithReflection(stack, ns, objSymbol, out operand))
                        return false;
                    return true;
                }

            }
            return false;
        }


        private static ErrorCollector Calculate( string left , string right , ExpressionScanner.TToken op , out ExecutionStack.Variable result )
        {
            switch (op)
            {
                case ExpressionScanner.TToken.ADD:
                    result = new ExecutionStack.Variable(left + right);
                    break;
                case ExpressionScanner.TToken.GT:
                    result = new ExecutionStack.Variable(String.Compare(left, right));
                    break;
                case ExpressionScanner.TToken.GET:
                    result = new ExecutionStack.Variable(String.Compare(left,right)>=0 ? 1.0:0.0 );
                    break;
                case ExpressionScanner.TToken.LT:
                    result = new ExecutionStack.Variable(String.Compare(right, left));
                    break;
                case ExpressionScanner.TToken.LET:
                    result = new ExecutionStack.Variable(String.Compare(left, right) > 0 ? 0.0 : 1.0);
                    break;
                case ExpressionScanner.TToken.EQ:
                    result = new ExecutionStack.Variable(String.Compare(left, right) == 0 ? 1.0 : 0.0);
                    break;
                case ExpressionScanner.TToken.NEQ:
                    result = new ExecutionStack.Variable(String.Compare(left, right) != 0 ? 1.0 : 0.0);
                    break;
                case ExpressionScanner.TToken.AND:
                case ExpressionScanner.TToken.OR:
                    result = new ExecutionStack.Variable(1.0f);
                    break;
                default:
                    result = ExecutionStack.Variable.MakeVoid();
                    return new ErrorCollector("String doesn't support this operation!");
            }
            return new ErrorCollector();
        }

        private static ErrorCollector Calculate( double left , double right , ExpressionScanner.TToken op , out ExecutionStack.Variable result )
        {
            switch (op)
            {
                case ExpressionScanner.TToken.ADD:
                    result = new ExecutionStack.Variable(left + right);
                    break;
                case ExpressionScanner.TToken.SUB:
                    result = new ExecutionStack.Variable(left - right);
                    break;
                case ExpressionScanner.TToken.MUL:
                    result = new ExecutionStack.Variable(left * right);
                    break;
                case ExpressionScanner.TToken.DIV:
                    if (right == 0.0f)
                    {
                        result = ExecutionStack.Variable.MakeVoid();
                        return new ErrorCollector("divider is zero");
                    }
                    result = new ExecutionStack.Variable(left / right);
                    break;
                case ExpressionScanner.TToken.GT:
                    result = new ExecutionStack.Variable( left > right ? 1.0f : 0.0f );
                    break;
                case ExpressionScanner.TToken.GET:
                    result = new ExecutionStack.Variable(left >= right ? 1.0f : 0.0f);
                    break;
                case ExpressionScanner.TToken.LT:
                    result = new ExecutionStack.Variable(left < right ? 1.0f : 0.0f);
                    break;
                case ExpressionScanner.TToken.LET:
                    result = new ExecutionStack.Variable(left <= right ? 1.0f : 0.0f);
                    break;
                case ExpressionScanner.TToken.EQ:
                    result = new ExecutionStack.Variable(left == right ? 1.0f : 0.0f);
                    break;
                case ExpressionScanner.TToken.NEQ:
                    result = new ExecutionStack.Variable(left != right ? 1.0f : 0.0f);
                    break;
                case ExpressionScanner.TToken.AND:
                    if (left != 0.0 && right != 0.0)
                        result = new ExecutionStack.Variable(1.0);
                    else
                        result = new ExecutionStack.Variable(0.0);
                    break;

                case ExpressionScanner.TToken.OR:
                    if( left == 0.0 && right == 0.0 )
                        result = new ExecutionStack.Variable(0.0);
                    else
                        result = new ExecutionStack.Variable(1.0);
                    break;
                default:
                    result = ExecutionStack.Variable.MakeVoid();
                    return new ErrorCollector("String doesn't support this operation!");
            }
            return new ErrorCollector();
        }


        // This function will only be called once the left operand and right operand is 
        // clear. For this function we do real operation, type casting. 
        // The casting is as below:
        // ----------------------------------------------------------------------------
        // If one operand is string, the other type will be casted to string and do the
        // calculation. The calculation for string will only supports +(concatenation).
        // ----------------------------------------------------------------------------
        // If both are numbers, every type of operation is supported.
        // If a user data is found, error takes place.
        
        private static ErrorCollector ExecuteArithmetic(ExecutionStack.Variable leftOp,
                                                               ExecutionStack.Variable rightOp,
                                                               ExpressionScanner.TToken op,
                                                               GlobalFunction func,
                                                               ExecutionStack stack,
                                                               out ExecutionStack.Variable result)
        {
            result = ExpressionInterpreter.ExecutionStack.Variable.MakeVoid();
            // Checking type
            if( leftOp.Type == ExecutionStack.Variable.VariableType.OBJECT || rightOp.Type == ExecutionStack.Variable.VariableType.OBJECT )
            {
                return new ErrorCollector("The type UserData/Object doesn't support arithmetic calculation!");
            }
            // For void type we allow comparison of EQ/NEQ, other operation is not supported
            else if( leftOp.Type == ExecutionStack.Variable.VariableType.VOID || rightOp.Type == ExecutionStack.Variable.VariableType.VOID )
            {
                switch(op)
                {
                    case ExpressionScanner.TToken.EQ:
                        result = new ExecutionStack.Variable( leftOp.Type == rightOp.Type ? 1.0f : 0.0f );
                        return new ErrorCollector();
                    case ExpressionScanner.TToken.NEQ:
                        result = new ExecutionStack.Variable( leftOp.Type != rightOp.Type ? 1.0f : 0.0f );
                        return new ErrorCollector();
                    default:
                        return new ErrorCollector("The type null only support !=/== operation!");
                }
            }
            // Here I just use a hand code if-else to do the type and operation. If more complicated type system is used
            // a more general way should be used to implement this function.
            else if (leftOp.Type == ExecutionStack.Variable.VariableType.STRING)
            {
                string leftValue;
                string rightValue;
                if (rightOp.Type == ExecutionStack.Variable.VariableType.NUMBER)
                {
                    double number;
                    if (!rightOp.AsNumber(out number))
                        return new ErrorCollector("Cannot parse as number");
                    rightValue = number.ToString();
                }
                else
                {
                    if (!rightOp.AsString(out rightValue))
                        return new ErrorCollector("Cannot parse as string");
                }
                if (!leftOp.AsString(out leftValue))
                    return new ErrorCollector("Cannot parse as string");

                // Calculate the value for this specific operator
                return Calculate(leftValue, rightValue, op,out result);
            }
            else
            {
                if (rightOp.Type == ExecutionStack.Variable.VariableType.NUMBER)
                {
                    double leftValue;
                    double rightValue;
                    if (!leftOp.AsNumber(out leftValue) ||  !rightOp.AsNumber(out rightValue))
                        return new ErrorCollector("Cannot parse as number!");
                    // Calculate the  value for this specific operator
                    return Calculate(leftValue, rightValue, op, out result);
                }
                else
                {
                    string leftValue;
                    string rightValue;
                    double dLeftValue;
                    if (!leftOp.AsNumber(out dLeftValue))
                        return new ErrorCollector("Cannot parse as number");
                    leftValue = dLeftValue.ToString();
                    if (!rightOp.AsString(out rightValue))
                        return new ErrorCollector("Cannot parse as string");
                    // Calculate the  value for this specific operator
                    return Calculate(leftValue, rightValue, op, out result);
                }
            }
        }

        private static ErrorCollector ExecuteArithmetic(ExpressionParser.AstNode left,
                                                               ExecutionStack.Variable right,
                                                               ExpressionScanner.TToken op,
                                                               GlobalFunction func,
                                                               ExecutionStack stack,Namespace ns,
                                                               out ExecutionStack.Variable result)
        {
            result = ExpressionInterpreter.ExecutionStack.Variable.MakeVoid();
            ExecutionStack.Variable leftOp;

            if (!FetchOperand(left, func, stack, ns , out leftOp))
            {
                return new ErrorCollector("Cannot parse left operand!");
            }
            return ExecuteArithmetic(leftOp, right, op, func, stack, out result);
        }

        private static ErrorCollector ExecuteArithmetic(ExecutionStack.Variable left,
                                                               ExpressionParser.AstNode right,
                                                               ExpressionScanner.TToken op,
                                                               GlobalFunction func,
                                                               ExecutionStack stack,Namespace ns,
                                                               out ExecutionStack.Variable result)
        {
            result = ExpressionInterpreter.ExecutionStack.Variable.MakeVoid();
            ExecutionStack.Variable rightOp;

            if (!FetchOperand(right, func, stack, ns , out rightOp))
            {
                return new ErrorCollector("Cannot parse right operand!");
            }
            return ExecuteArithmetic(left, rightOp, op, func, stack, out result);
        }

        private static ErrorCollector ExecuteArithmetic(ExpressionParser.AstNode left,
                                                               ExpressionParser.AstNode right,
                                                               ExpressionScanner.TToken op,
                                                               GlobalFunction func,
                                                               ExecutionStack stack,Namespace ns,
                                                               out ExecutionStack.Variable result)
        {
            result = ExpressionInterpreter.ExecutionStack.Variable.MakeVoid();
            ExecutionStack.Variable leftOp;
            ExecutionStack.Variable rightOp;

            if (!FetchOperand(left, func, stack, ns , out leftOp) || !FetchOperand(right, func, stack, ns , out rightOp))
            {
                return new ErrorCollector("Cannot parse operand!");
            }
            return ExecuteArithmetic(leftOp, rightOp, op, func, stack, out result);
        }

        private static ErrorCollector ExecuteUnaryNode( ExpressionParser.AstNode_Unary node, 
                                                        GlobalFunction func,ExecutionStack stack, Namespace ns ,
                                                        out ExecutionStack.Variable result )
        {
            ErrorCollector error = ExecuteOperand(node.Operand, func, stack, ns , out result);
            if (!error.Success)
                return error;

            switch(node.Operator.Token)
            {
                case ExpressionScanner.TToken.SUB:
                    // negative value 
                    switch(result.Type)
                    {
                        case ExecutionStack.Variable.VariableType.NUMBER:
                            double num;
                            Debug.Assert(result.AsNumber(out num));
                            result = new ExecutionStack.Variable(-num);
                            return new ErrorCollector();
                        case ExecutionStack.Variable.VariableType.STRING:
                        case ExecutionStack.Variable.VariableType.VOID:
                        case ExecutionStack.Variable.VariableType.OBJECT:
                            return new ErrorCollector("String/void/Object doesn't support negative operator!");
                        default:
                            Debug.Assert(false);
                            return null;
                    }
                case ExpressionScanner.TToken.NOT:
                    switch(result.Type)
                    {
                        case ExecutionStack.Variable.VariableType.NUMBER:
                            double num;
                            Debug.Assert(result.AsNumber(out num));
                            result = new ExecutionStack.Variable(num ==0.0 ? 1.0 : 0.0);
                            return new ErrorCollector();
                        case ExecutionStack.Variable.VariableType.STRING:
                            result = new ExecutionStack.Variable(0.0);
                            return new ErrorCollector();
                        case ExecutionStack.Variable.VariableType.VOID:
                            result = new ExecutionStack.Variable(1.0);
                            return new ErrorCollector();
                        case ExecutionStack.Variable.VariableType.OBJECT:
                            return new ErrorCollector("Object is not supported for negative operator!");
                        default:
                            Debug.Assert(false);
                            return null;
                    }

                default:
                    Debug.Assert(false);
                    return null;
            }
        }

        private static ErrorCollector ExecuteOperand(ExpressionParser.AstNode n, GlobalFunction func,
                                                     ExecutionStack stack, Namespace ns , 
                                                     out ExecutionStack.Variable result)
        {
            ExpressionParser.AstNode.NodeType nodeType = n.Type;
            ErrorCollector ret;
            result = ExpressionInterpreter.ExecutionStack.Variable.MakeVoid();

            switch (nodeType)
            {
                case ExpressionParser.AstNode.NodeType.OBJECT:
                    return ExecuteObjectScope((ExpressionParser.AstNode_Object)(n), func, stack, ns, out result);
                case ExpressionParser.AstNode.NodeType.FUNCTION_CALL:
                    return ExecuteFunctionCall((ExpressionParser.AstNode_FunctionCall)(n), func, stack, ns , out result);
                case ExpressionParser.AstNode.NodeType.UNARY:
                    return ExecuteUnaryNode((ExpressionParser.AstNode_Unary)(n), func, stack, ns , out result);
                case ExpressionParser.AstNode.NodeType.ARITHMETIC:
                    ExpressionParser.AstNode_Arithmetic node = (ExpressionParser.AstNode_Arithmetic)n;
                    ExpressionParser.AstNode left = node.LeftChild;
                    ExpressionParser.AstNode right = node.RightChild;
                    if (IsBaseNode(left))
                    {
                        if (IsBaseNode(right))
                        {
                            return ExecuteArithmetic(left, right, node.Operator.Token, func, stack, ns, out result);
                        }
                        else
                        {
                            ExecutionStack.Variable rightResult;
                            ret = ExecuteOperand(right, func, stack, ns,out rightResult);
                            if (!ret.Success)
                                return ret;
                            return ExecuteArithmetic(left, rightResult, node.Operator.Token, func, stack, ns, out result);
                        }
                    }
                    else
                    {
                        if (IsBaseNode(right))
                        {
                            ExecutionStack.Variable leftResult;
                            ret = ExecuteOperand(left, func, stack, ns,out leftResult);
                            if (!ret.Success)
                                return ret;
                            return ExecuteArithmetic(leftResult, right, node.Operator.Token, func, stack, ns,out result);
                        }
                        else
                        {
                            ExecutionStack.Variable leftResult;
                            ExecutionStack.Variable rightResult;
                            ret = ExecuteOperand(left, func, stack, ns,out leftResult);
                            if (!ret.Success)
                                return ret;
                            ret = ExecuteOperand(right, func, stack, ns,out rightResult);
                            if (!ret.Success)
                                return ret;
                            return ExecuteArithmetic(leftResult, rightResult, node.Operator.Token, func, stack, out result);
                        }
                    }

                case ExpressionParser.AstNode.NodeType.CONSTANT:
                case ExpressionParser.AstNode.NodeType.VARIABLE:
                    if (!FetchOperand(n, func, stack, ns,out result))
                        return new ErrorCollector("Cannot fetch constant/variable value");
                    return new ErrorCollector();
                default:
                    return new ErrorCollector("Unknown node type");
            }
        }

        private static bool SetValueWithReflection(ExecutionStack stack, Namespace ns, string key, ExecutionStack.Variable variable )
        {
            ObjectName objName = new ObjectName(key);
            object obj = null;
            while (objName.HasMoreNames())
            {
                string name = objName.NextName();
                // Grab the variable and check weather it is an Object or not
                obj = stack.LookUpAsObject(ns.GetName(name));
                if (obj != null)
                {
                    break;
                }
            }
            if (obj == null) return false;
            // Cut the previous name right now.
            objName.Cut();

            if (!objName.HasMoreNames())
                return false;
            string fieldName = objName.NextName();
            // Now let's grab the value of obj
            while (true)
            {
                if (obj == null) return false;

                FieldInfo fieldInfo = obj.GetType().GetField(fieldName);
                if (fieldInfo == null)
                {
                    PropertyInfo propertyInfo = obj.GetType().GetProperty(fieldName);
                    if (propertyInfo == null)
                        return false;
                    // Checking if we have more field names or not
                    if (objName.HasMoreNames())
                    {
                        fieldName = objName.NextName();
                        // Moving our object chain further
                        obj = propertyInfo.GetValue(obj, null);
                        continue;
                    }
                    else
                    {
                        switch (variable.Type)
                        {
                            case ExecutionStack.Variable.VariableType.NUMBER:
                                double number;
                                Debug.Assert(variable.AsNumber(out number));
                                try
                                {
                                    propertyInfo.SetValue(obj, Convert.ChangeType(number, propertyInfo.PropertyType), null);
                                }
                                catch (Exception)
                                {
                                    return false;
                                }
                                return true;
                            case ExecutionStack.Variable.VariableType.STRING:
                                string str;
                                Debug.Assert(variable.AsString(out str));
                                try
                                {
                                    propertyInfo.SetValue(obj, Convert.ChangeType(str, propertyInfo.PropertyType), null);
                                }
                                catch (Exception e)
                                {
                                    return false;
                                }
                                return true;
                            case ExecutionStack.Variable.VariableType.OBJECT:
                                object o = variable.AsObject();
                                Debug.Assert(o != null);
                                try
                                {
                                    propertyInfo.SetValue(obj, o, null);
                                }
                                catch (Exception)
                                {
                                    return false;
                                }
                                return true;
                            default:
                                return false;
                        }
                    }
                }
                else
                {
                    if (objName.HasMoreNames())
                    {
                        fieldName = objName.NextName();
                        obj = fieldInfo.GetValue(obj);
                        continue;
                    }
                    else
                    {
                        switch (variable.Type)
                        {
                            case ExecutionStack.Variable.VariableType.NUMBER:
                                double number;
                                Debug.Assert(variable.AsNumber(out number));
                                try
                                {
                                    fieldInfo.SetValue(obj, Convert.ChangeType(number,fieldInfo.FieldType));
                                }
                                catch (Exception)
                                {
                                    return false;
                                }
                                return true;
                            case ExecutionStack.Variable.VariableType.STRING:
                                string str;
                                Debug.Assert(variable.AsString(out str));
                                try
                                {
                                    fieldInfo.SetValue(obj, Convert.ChangeType(str, fieldInfo.FieldType));
                                }
                                catch (Exception)
                                {
                                    return false;
                                }
                                return true;
                            case ExecutionStack.Variable.VariableType.OBJECT:
                                object o = variable.AsObject();
                                Debug.Assert(o != null);
                                try
                                {
                                    fieldInfo.SetValue(obj,o);
                                }
                                catch (Exception)
                                {
                                    return false;
                                }
                                return true;
                            default:
                                return false;
                        }
                    }
                }
            }
        }

        private static ErrorCollector ExecuteAssignment(ExpressionParser.AstNode_Assignment node, GlobalFunction func, 
                                                        ExecutionStack stack , Namespace ns )
        {
            ExpressionScanner.LexInfo leftInfo = node.Left;
            ErrorCollector ret;
            string leftKey;
            if (!leftInfo.AsVariable(out leftKey))
            {
                ErrorCollector error = new ErrorCollector();
                error.Message = "Cannot parse assignment left handle side variable name";
                error.Success = false;
                return error;
            }
            ExecutionStack.Variable rightResult;
            ret = ExecuteOperand(node.Right, func, stack, ns,out rightResult);
            if (!ret.Success)
                return ret;
            // Try to set the value right now.
            string objSymbol = IsObjectSymbol(leftKey);
            if( objSymbol == null )
            {
                stack.Add(ns.GetName(leftKey), rightResult);
            }
            else
            {
                if (!SetValueWithReflection(stack, ns, objSymbol, rightResult))
                    return new ErrorCollector("Cannot set value for object type!");
            }
            return new ErrorCollector();
        }
        private static ErrorCollector ExecuteBranch(ExpressionParser.AstNode_Branch node , GlobalFunction func, ExecutionStack stack , Namespace ns )
        {
            bool all_fail = true;
            ErrorCollector err;

            foreach( ExpressionParser.AstNode_Branch.Branch branch in node.Branches )
            {
                if( branch.Condition.Type == ExpressionParser.AstNode_Branch.NodeType.NULL )
                {
                    if( all_fail )
                    {
                        err = Execute(branch.Code, func, stack);
                        if (!err.Success)
                            return err;
                    }
                }
                else 
                {
                    ExecutionStack.Variable result;
                    err = ExecuteOperand(branch.Condition, func, stack, ns , out result);
                    if (!err.Success)
                        return err;
                    bool boolean;
                    if (!result.AsBoolean(out boolean))
                        return new ErrorCollector("Cannot convert condition to boolean value.");
                    if(boolean)
                    {
                        err = Execute(branch.Code, func, stack);
                        if (!err.Success)
                            return err;
                        all_fail = false;
                    }

                }
            }

            return new ErrorCollector();
        }

        private static ErrorCollector ExecuteScope( ExpressionParser.AstNode_Scope node, GlobalFunction func , ExecutionStack stack , Namespace ns )
        {
            string scopeName;
            if (!node.ScopeName.AsVariable(out scopeName))
                return new ErrorCollector("Cannot get variable name!");
            ns.EnterNamespace(scopeName);
            ErrorCollector err = Execute(node.Code, func, stack, ns);
            ns.LeaveNamespace();
            return err;
        }

        private static Type ExecuteObjectScope_ClassName(ExpressionParser.AstNode_Object node, GlobalFunction func , 
            ExecutionStack stack ,  Namespace ns , ErrorCollector collector)
        {
            string className;
            if (!node.ClassName.AsVariable(out className))
                return null;
            // Right now we are able to grab the class name and then find out its Type 
            Type ret = Assembly.GetExecutingAssembly().GetType(className);
            if( ret == null ) 
            {
                collector.Message = "Cannot find type by reflection with class name: " + className + ".You must specified a full class name!.";
                return null;
            }
            return ret;
        }

        private static object[] FormatParameterList(ExpressionParser.AstNode_FunctionCall node, GlobalFunction func,ExecutionStack stack, Namespace ns)
        {
            object[] ret = new object[node.Parameters.Count];
            for( int i = 0 ; i < ret.Length ; ++i )
            {
                ExecutionStack.Variable result;
                ErrorCollector err = ExecuteOperand(node.Parameters[i], func, stack, ns, out result);
                if (!err.Success)
                    return null;
                ret[i] = result.ToObject();
            }
            return ret;
        }

        private static ErrorCollector ExecuteObjectScope_CallCtor(ExpressionParser.AstNode_Object node, GlobalFunction func, 
            ExecutionStack stack, Namespace ns , Type classType , out object obj)
        {
            obj = null;
            if (node.Constructor == null)
            {
                try
                {
                    obj = Activator.CreateInstance(classType);
                }
                catch (Exception)
                {
                    return new ErrorCollector("Cannot create object using its default constructor," +
                    "but no specified constructor is set in attributes!class:" + classType.Name);
                }
            }
            else
            {
                object[] parList = FormatParameterList(node.Constructor, func, stack, ns);
                if (parList.Length > 0)
                {
                    if (parList == null)
                        return new ErrorCollector("Cannot evaluate constructor parameter list for class:" + classType.Name);
                    try
                    {
                        obj = Activator.CreateInstance(classType, parList);
                    }
                    catch (Exception)
                    {
                        return new ErrorCollector("Cannot create object using its specified constructor," +
                        "but no specified constructor is set in attributes!class:" + classType.Name);
                    }
                }
                else
                {
                    try
                    {
                        obj = Activator.CreateInstance(classType);
                    }
                    catch (Exception)
                    {
                        return new ErrorCollector("Cannot create object using its default constructor," +
                        "but no specified constructor is set in attributes!class:" + classType.Name);
                    }
                }
            }
            stack.Add(ns.GetName("this"), new ExecutionStack.Variable(obj));
            return new ErrorCollector();
        }

        private static ErrorCollector ExecuteObjectScope_CallPost(ExpressionParser.AstNode_Object node, GlobalFunction func, 
            ExecutionStack stack, Namespace ns, object obj,Type classType)
        {
            // Setting this pointer inside of this scope and then use it.
            if(node.PostInit != null)
            {
                object[] parameterList = FormatParameterList(node.PostInit, func, stack, ns);
                if (parameterList == null)
                    return new ErrorCollector("Cannot evaluate constructor parameter list for class:" + classType.FullName);
                string functionName;
                if (!node.PostInit.FunctionName.AsVariable(out functionName))
                    return new ErrorCollector("Cannot parse function name of that post function!class:" + classType.FullName);
                MethodInfo postFunc = classType.GetMethod(functionName, BindingFlags.Public | BindingFlags.Instance);
                if (postFunc == null)
                    return new ErrorCollector();
                try
                {
                    postFunc.Invoke(obj, parameterList);
                }
                catch (Exception)
                {
                    return new ErrorCollector("Cannot invoke post function.Maybe type conversion is not allowed here!");
                }
            }

            return new ErrorCollector();
        }

        private static ErrorCollector ExecuteObjectScope(ExpressionParser.AstNode_Object node, GlobalFunction func , ExecutionStack stack , 
            Namespace ns,out ExecutionStack.Variable result)
        {
            result = default(ExecutionStack.Variable);
            ErrorCollector collector = new ErrorCollector();
            // 1. Get the class type of that object
            Type classType = ExecuteObjectScope_ClassName(node, func, stack,ns,collector);
            if (classType == null)
                return collector;
            // Enter that namespace for that specific class
            ns.EnterNamespace(classType.FullName);
            // 2. Initialize that class in scope
            object obj;
            collector = ExecuteObjectScope_CallCtor(node, func, stack, ns, classType, out obj);
            if (!collector.Success)
                return collector;
            // 3. Execute the specific scope code here
            collector = Execute(node.Code, func, stack, ns);
            if (!collector.Success)
                return collector;
            // 4. Call post if we have one
            collector = ExecuteObjectScope_CallPost(node, func, stack, ns, obj, classType);
            if (!collector.Success)
                return collector;
            // Lastly remove the this pointer here
            stack.Remove(ns.GetName("this"));
            result = new ExecutionStack.Variable(obj);
            // Leave the namespace for that specific class
            ns.LeaveNamespace();
            return collector;
        }

        private static ErrorCollector Execute( ExpressionParser.AstNode node, GlobalFunction func, ExecutionStack stack , Namespace ns )
        {
            
            switch( node.Type )
            {
                case ExpressionParser.AstNode.NodeType.ASSIGNMENT:
                     return ExecuteAssignment((ExpressionParser.AstNode_Assignment)node, func, stack,ns);
                case ExpressionParser.AstNode.NodeType.FUNCTION_CALL:
                     ExecutionStack.Variable variable;
                     return ExecuteFunctionCall((ExpressionParser.AstNode_FunctionCall)node, func, stack, ns ,out variable );
                case ExpressionParser.AstNode.NodeType.BRANCH:
                     return ExecuteBranch((ExpressionParser.AstNode_Branch)node, func, stack , ns);
                case ExpressionParser.AstNode.NodeType.SCOPE:
                     return ExecuteScope((ExpressionParser.AstNode_Scope)node, func, stack, ns);
                default:
                     return new ErrorCollector("Unknown node type");
            }
        }

        private static ErrorCollector Execute(List<ExpressionParser.AstNode> nodes, GlobalFunction func, ExecutionStack stack ,Namespace ns)
        {
            ErrorCollector ret;
            for (int i = 0; i < nodes.Count; ++i)
            {
                ret = Execute( nodes[i], func, stack ,ns);
                if (!ret.Success)
                    return ret;
            }
            return new ErrorCollector();
        }

        public static ErrorCollector Execute( List<ExpressionParser.AstNode> nodes, GlobalFunction func, ExecutionStack stack )
        {
            return Execute(nodes, func, stack, new Namespace());
        }

        public static ErrorCollector Execute(string script, GlobalFunction func, ExecutionStack stack )
        {
            ErrorCollector errorCollector = new ErrorCollector();
            List<ExpressionParser.AstNode> parserResult = ExpressionParser.Compile(script, errorCollector);
            if (!errorCollector.Success)
                return errorCollector;
            return Execute(parserResult, func, stack);
        }

        public static ErrorCollector ExecuteFromFile(string file,GlobalFunction func, ExecutionStack stack)
        {
            System.IO.StreamReader f = new System.IO.StreamReader(file);
            return Execute(f.ReadToEnd(), func, stack);
        }
    }
}
